package org.dromara.system.service.impl;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.exception.BusinessException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.system.converter.MachineConverters;
import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.WechatConfig;
import org.dromara.system.domain.bo.GetOpenIdBo;
import org.dromara.system.domain.bo.QueryOpenIdByCodeBo;
import org.dromara.system.domain.bo.QueryWxConfigByInvitationCodeBo;
import org.dromara.system.domain.bo.WechatConfigBo;
import org.dromara.system.domain.vo.QueryWxJsapiTicketVo;
import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.domain.vo.WechatConfigVo;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.system.mapper.WechatConfigMapper;
import org.dromara.system.service.ISysUserService;
import org.dromara.system.service.IWechatConfigService;
import org.dromara.system.util.JsonUtil;
import org.springframework.stereotype.Service;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.PrintWriter;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 微信Service业务层处理
 *
 * @author Lion Li
 * @date 2025-07-15
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class WechatConfigServiceImpl extends ServiceImpl<WechatConfigMapper, WechatConfig> implements IWechatConfigService {

    private static final String WX_ACODEUN_LIMIT_URL = "https://api.weixin.qq.com/wxa/getwxacodeunlimit?access_token=%s";

    private final ISysUserService sysUserService;

    @Resource
    private final SysUserMapper sysUserMapper;

    public static String getminiqrQr(String sceneStr, String accessToken, String page) {
        HttpURLConnection httpURLConnection = null;
        try {
            URL url = new URL(String.format(WX_ACODEUN_LIMIT_URL, accessToken));
            httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("POST");// 提交模式
            // 发送POST请求必须设置如下两行
            httpURLConnection.setDoOutput(true);
            httpURLConnection.setDoInput(true);
            // 获取URLConnection对象对应的输出流
            PrintWriter printWriter = new PrintWriter(httpURLConnection.getOutputStream());
            // 发送请求参数
            JSONObject paramJson = new JSONObject();
            paramJson.put("scene", sceneStr);
            paramJson.put("page", page);
            printWriter.write(paramJson.toString());
            // flush输出流的缓冲
            printWriter.flush();
            //开始获取数据
            BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream());
            try (InputStream is = httpURLConnection.getInputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
                byte[] buffer = new byte[1024];
                int len = -1;
                while ((len = is.read(buffer)) != -1) {
                    baos.write(buffer, 0, len);
                }
                return java.util.Base64.getEncoder().encodeToString(baos.toByteArray());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (httpURLConnection != null) {
                httpURLConnection.disconnect();
            }
        }
        return null;
    }

//    public static void main(String[] args) {
//        HashMap<String, Object> hashMap = new HashMap<>();
//        hashMap.put("grant_type", "client_credential");
//        hashMap.put("appid", "wx3951e5ec51d7adf9");
//        hashMap.put("secret", "881a7143a4fa3bc09192aa32873e0709");
//        String s = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", hashMap);
//        Map<Object, Object> map = JsonUtil.toMap(s);
//        String accessToken = (String) map.get("access_token");
//        String string = getminiqrQr("123456", accessToken, "pages/profile/register/register");
//        System.out.println(string);
//
//    }

    /**
     * 查询微信
     *
     * @param id 主键
     * @return 微信
     */
    @Override
    public WechatConfigVo queryById(Long id) {
        return baseMapper.selectVoById(id);
    }

    /**
     * 分页查询微信列表
     *
     * @param bo        查询条件
     * @param pageQuery 分页参数
     * @return 微信分页列表
     */
    @Override
    public TableDataInfo<WechatConfigVo> queryPageList(WechatConfigBo bo, PageQuery pageQuery) {
        LambdaQueryWrapper<WechatConfig> lqw = buildQueryWrapper(bo);
        Page<WechatConfigVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
        return TableDataInfo.build(result);
    }

    /**
     * 查询符合条件的微信列表
     *
     * @param bo 查询条件
     * @return 微信列表
     */
    @Override
    public List<WechatConfigVo> queryList(WechatConfigBo bo) {
        LambdaQueryWrapper<WechatConfig> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<WechatConfig> buildQueryWrapper(WechatConfigBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<WechatConfig> lqw = Wrappers.lambdaQuery();
        lqw.orderByAsc(WechatConfig::getId);
        lqw.eq(bo.getUserId() != null, WechatConfig::getUserId, bo.getUserId());
        lqw.eq(StringUtils.isNotBlank(bo.getAppId()), WechatConfig::getAppId, bo.getAppId());
        lqw.eq(StringUtils.isNotBlank(bo.getAppSecret()), WechatConfig::getAppSecret, bo.getAppSecret());
        lqw.eq(StringUtils.isNotBlank(bo.getMchId()), WechatConfig::getMchId, bo.getMchId());
        lqw.eq(StringUtils.isNotBlank(bo.getMchKey()), WechatConfig::getMchKey, bo.getMchKey());
        lqw.eq(StringUtils.isNotBlank(bo.getPrivateKey()), WechatConfig::getPrivateKey, bo.getPrivateKey());
        lqw.eq(StringUtils.isNotBlank(bo.getMerchantSerialNumber()), WechatConfig::getMerchantSerialNumber, bo.getMerchantSerialNumber());
        lqw.eq(StringUtils.isNotBlank(bo.getVThreeMchKey()), WechatConfig::getVThreeMchKey, bo.getVThreeMchKey());
        return lqw;
    }

    /**
     * 新增微信
     *
     * @param bo 微信
     * @return 是否新增成功
     */
    @Override
    public Boolean insertByBo(WechatConfigBo bo) {
        WechatConfig add = MapstructUtils.convert(bo, WechatConfig.class);
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setId(add.getId());
        }
        return flag;
    }

    /**
     * 修改微信
     *
     * @param bo 微信
     * @return 是否修改成功
     */
    @Override
    public Boolean updateByBo(WechatConfigBo bo) {
        WechatConfig update = MapstructUtils.convert(bo, WechatConfig.class);
        validEntityBeforeSave(update);
        return baseMapper.updateById(update) > 0;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(WechatConfig entity) {
        //TODO 做一些数据校验,如唯一约束
    }

    /**
     * 校验并批量删除微信信息
     *
     * @param ids     待删除的主键集合
     * @param isValid 是否进行有效性校验
     * @return 是否删除成功
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if (isValid) {
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteByIds(ids) > 0;
    }

    @Override
    public String createWechatInvitation() {
        SysUserVo sysUserVo = sysUserService.selectUserById(LoginHelper.getUserId());
        WechatConfig wechatConfig = lambdaQuery().eq(WechatConfig::getUserId, sysUserVo.getOneId()).one();
        BusinessException.isNull(wechatConfig, "代理商微信未配置！联系管理员处理！");
        HashMap<String, Object> hashMap = new HashMap<>();
        hashMap.put("grant_type", "client_credential");
        hashMap.put("appid", wechatConfig.getAppId());
        hashMap.put("secret", wechatConfig.getAppSecret());
        String s = HttpUtil.get("https://api.weixin.qq.com/cgi-bin/token", hashMap);
        Map<Object, Object> map = JsonUtil.toMap(s);
        String accessToken = (String) map.get("access_token");
        return getminiqrQr(sysUserVo.getInvitationCode(), accessToken, wechatConfig.getHomePageUrl());
    }

    @Override
    public Boolean getOpenId(GetOpenIdBo bo) {
        String code = bo.getCode();
        Long userId = bo.getUserId();
        //查询用户openid
        log.info("code={},userName={}", code, userId);
        if (ObjectUtil.isNull(code) || ObjectUtil.isNull(userId)) {
            return null;
        }
        SysUserVo sysUserVo = sysUserService.selectUserById(userId);
        WechatConfig wechatConfig = lambdaQuery().eq(WechatConfig::getUserId, sysUserVo.getOneId()).one();
        BusinessException.isNull(wechatConfig, "未查询到微信配置信息");
        String openIdPath = "https://api.weixin.qq.com/sns/oauth2/access_token";
        String s1 = openIdPath + "?appid=" + wechatConfig.getAppId() + "&secret=" + wechatConfig.getAppSecret()
            + "&code=" + code + "&grant_type=authorization_code";
        log.info("getOpenId请求openid接口参数{}", s1);
        String s = HttpUtil.get(s1);
        Map<String, Object> objectMap = JsonUtil.toMap(s);
        log.info("getOpenId返回objectMap={}", JSON.toJSONString(objectMap));
        String openId = Convert.toStr(objectMap.get("openid"));
        BusinessException.isTrue(StrUtil.isBlank(openId), "用户openId获取失败");
        LambdaUpdateWrapper<SysUser> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(SysUser::getUserId, sysUserVo.getUserId());
        wrapper.set(SysUser::getOpenId, openId);
        sysUserMapper.update(wrapper);

        return Boolean.TRUE;
    }

    @Override
    public String queryOpenIdByCode(QueryOpenIdByCodeBo bo) {
        log.info("queryOpenIdByCode入参等于={}", JSONUtil.toJsonStr(bo));
        SysUserVo sysUserVo = sysUserService.selectUserById(bo.getUserId());

        String invitationCode = bo.getInvitationCode();
        BusinessException.isTrue(StrUtil.isBlank(invitationCode), "invitationCode不能为空！");
        SysUserVo sysUserInvitationCodeVo = sysUserService.selectUserByInvitationCode(invitationCode);
        if (ObjectUtil.notEqual(sysUserVo.getOneId(), sysUserInvitationCodeVo.getOneId())) {
            BusinessException.isTrue(Boolean.TRUE, "账户不存在！");
        }
        WechatConfig wechatConfig = lambdaQuery().eq(WechatConfig::getUserId, sysUserVo.getOneId()).one();
        BusinessException.isNull(wechatConfig, "未查询到微信配置信息");
        String openIdPath = "https://api.weixin.qq.com/sns/oauth2/access_token";
        String s1 = openIdPath + "?appid=" + wechatConfig.getAppId() + "&secret=" + wechatConfig.getAppSecret()
            + "&code=" + bo.getCode() + "&grant_type=authorization_code";
        log.info("queryOpenIdByCode请求openid接口参数{}", s1);
        String s = HttpUtil.get(s1);
        Map<String, Object> objectMap = JsonUtil.toMap(s);
        log.info("queryOpenIdByCode返回objectMap={}", JSON.toJSONString(objectMap));
        String openId = Convert.toStr(objectMap.get("openid"), "");
//        BusinessException.isTrue(StrUtil.isBlank(openId), "用户OPENID获取失败");
        return openId;
    }

    @Override
    public String queryOpenIdBySmallProgram(QueryOpenIdByCodeBo bo) {
        SysUserVo sysUserVo = sysUserService.selectUserById(bo.getUserId());
        WechatConfig wechatConfig = lambdaQuery().eq(WechatConfig::getUserId, sysUserVo.getOneId()).one();
        String openIdParam = "https://api.weixin.qq.com/sns/jscode2session" + "?appid=" + wechatConfig.getAppId() + "&secret=" + wechatConfig.getAppSecret()
            + "&js_code=" + bo.getCode() + "&grant_type=authorization_code";
        log.info("请求OPENID接口入口参数{}", openIdParam);
        String s = HttpUtil.get(openIdParam);
        Map<String, Object> objectMap = JsonUtil.toMap(s);
        log.info("请求OPENID接口返回参数={}", JSON.toJSONString(objectMap));
        String openId = Convert.toStr(objectMap.get("openid"));
        BusinessException.isTrue(com.baomidou.mybatisplus.core.toolkit.StringUtils.isBlank(openId), "用户OPENID获取失败");
        return openId;
    }

    @Override
    public WechatConfigVo queryWxConfigByInvitationCode(QueryWxConfigByInvitationCodeBo bo) {
        String invitationCode = bo.getInvitationCode();
        SysUserVo sysUserVo = sysUserService.selectUserByInvitationCode(invitationCode);
        BusinessException.isNull(sysUserVo, "未查询到相关参数");
        WechatConfig wechatConfig = lambdaQuery().eq(WechatConfig::getUserId, sysUserVo.getOneId()).one();
        return MachineConverters.INSTANCE.wechatConfigToVo(wechatConfig);
    }

    @Override
    public QueryWxJsapiTicketVo queryWxJsapiTicket(QueryWxConfigByInvitationCodeBo bo) {
        SysUserVo sysUserVo = sysUserService.selectUserByInvitationCode(bo.getInvitationCode());
        BusinessException.isNull(sysUserVo, "未查询到相关参数");
        WechatConfig wechatConfig = lambdaQuery().eq(WechatConfig::getUserId, sysUserVo.getOneId()).one();
        String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=" + "client_credential"
            + "&appid=" + wechatConfig.getAppId() + "&secret=" + wechatConfig.getAppSecret();
        // 获取accessToken
        String accessTokenBody = HttpUtil.get(accessTokenUrl);
        log.info("获取accessToken{}", accessTokenBody);
        JSONObject jsonObject = JSONObject.parseObject(accessTokenBody);
        String accessToken = null;
        try {
            accessToken = (String) jsonObject.get("access_token");
            log.info("accessToken:{}", accessToken);
        } catch (Exception e) {
            log.error("获取accessToken异常");
        }
        String getTicketUrl = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=" + accessToken + "&type=jsapi";
        // 获取getTicketResult
        String getTicketResult = HttpUtil.get(getTicketUrl);
        JSONObject ticketResultObject = JSONObject.parseObject(getTicketResult);
        String ticket = null;
        try {
            ticket = (String) ticketResultObject.get("ticket");
            log.info("ticket:{}", ticket);
        } catch (Exception e) {
            log.error("获取ticket异常");
        }
        long timestamp = System.currentTimeMillis() / 1000;
        String noncestr = RandomUtil.randomString(6);
        String signStr = "jsapi_ticket=" +
            ticket +
            "&noncestr=" +
            noncestr +
            "&timestamp=" +
            timestamp +
            "&url=" +
            bo.getUrl();
        log.info("signStr:{}", signStr);
        String signature = encryptSHA(signStr);
        return QueryWxJsapiTicketVo.builder()
            .timestamp(timestamp)
            .ticket(ticket)
            .noncestr(noncestr)
            .signature(signature)
            .appId(wechatConfig.getAppId())
            .build();
    }

    private static String encryptSHA(String signStr) {
        StringBuffer hexValue = new StringBuffer();
        MessageDigest sha = null;
        try {
            sha = MessageDigest.getInstance("SHA-1");
            byte[] byteArray = signStr.getBytes(StandardCharsets.UTF_8);
            byte[] md5Bytes = sha.digest(byteArray);
            for (int i = 0; i < md5Bytes.length; i++) {
                int val = ((int) md5Bytes[i]) & 0xff;
                if (val < 16) {
                    hexValue.append("0");
                }
                hexValue.append(Integer.toHexString(val));
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "";
        }
        return hexValue.toString();
    }

}
